//
//  AVAnimatorLayer.m
//
//  Created by Moses DeJong on 3/18/09.
//
//  License terms defined in License.txt.

#import <QuartzCore/QuartzCore.h>

#import "AVAnimatorMedia.h"

// private properties declaration for AVAnimatorLayer class
#import "AVAnimatorLayerPrivate.h"

// private method in Media class
#include "AVAnimatorMediaPrivate.h"

#if __has_feature(objc_arc)
#else
#import "AutoPropertyRelease.h"
#endif // objc_arc

#import "AVFrame.h"

// AVAnimatorLayer class

@implementation AVAnimatorLayer

// public properties

@synthesize layerObj = m_layerObj;
@synthesize mediaObj = m_mediaObj;
@synthesize frameObj = m_frameObj;

- (void) dealloc {
  // Detach but don't bother making a copy of the final image
  
  if (self.mediaObj) {
    [self.mediaObj detachFromRenderer:self copyFinalFrame:FALSE];
  }
  
  self.layer.contents = nil;
  
#if __has_feature(objc_arc)
#else
  [AutoPropertyRelease releaseProperties:self thisClass:AVAnimatorLayer.class];
  [super dealloc];
#endif // objc_arc
}

// static ctor

+ (AVAnimatorLayer*) aVAnimatorLayer:(CALayer*)layer
{
  NSAssert(layer, @"layer");
  AVAnimatorLayer *obj = [[AVAnimatorLayer alloc] init];
#if __has_feature(objc_arc)
#else
  obj = [obj autorelease];
#endif // objc_arc
  obj.layerObj = layer;
  return obj;  
}

// Invoked with TRUE argument once renderer has been attached to loaded media,
// otherwise FALSE is passed to indicate the renderer could not be attached

- (void) mediaAttached:(BOOL)worked
{
  if (worked) {
    NSAssert(self.media, @"media is nil");
    NSAssert(self.media.frameDecoder, @"frameDecoder is nil");

    self->mediaDidLoad = TRUE;
  } else {
    self.mediaObj = nil;
    self->mediaDidLoad = FALSE;
  }
  
	return;
}

- (void) attachMedia:(AVAnimatorMedia*)inMedia
{
#if defined(DEBUG)
  assert([NSThread currentThread] == [NSThread mainThread]);
#endif // DEBUG
  
  AVAnimatorMedia *currentMedia = self.mediaObj;
  
  if (currentMedia == inMedia) {
    // Detaching and the reattaching the same media is a no-op
    return;
  }
  
  if (inMedia == nil) {
    // Detach case, not attaching another media object so copy
    // the last rendered frame.
    
    [currentMedia detachFromRenderer:self copyFinalFrame:TRUE];
    self.mediaObj = nil;
    self.frameObj = nil;
    self->mediaDidLoad = FALSE;
    return;
  }
  
  [currentMedia detachFromRenderer:self copyFinalFrame:FALSE];
  self.mediaObj = inMedia;
  self.frameObj = nil;
  self->mediaDidLoad = FALSE;
  [inMedia attachToRenderer:self];
  return;
}

// Implement read-only property for use outside this class

- (AVAnimatorMedia*) media
{
  return self->m_mediaObj;
}

- (CALayer*) layer
{
  return self->m_layerObj;
}

// This method is invoked as part of the AVAnimatorMediaRendererProtocol when
// a new frame is generated by the media. Note that we only need to
// set the contents of the CALayer when the image actually
// changes. A duplicate frame would contain the same image data as the previous
// frame and redrawing would be a serious performance issue.
// Rendering the CGImageRef is handled by the CALayer class.

// setter for obj.AVFrame property

- (void) setAVFrame:(AVFrame*)inFrame
{
  if (inFrame == nil) {
    self.frameObj = nil;
    self.layer.contents = nil;
  } else {
    self.frameObj = inFrame;
    if (inFrame.isDuplicate == FALSE) {
      UIImage *image = inFrame.image;
      CGImageRef cgImage = image.CGImage;
      
      self.layer.contents =
#if __has_feature(objc_arc)
    (__bridge id) cgImage;
#else
    (id) cgImage;
#endif // objc_arc
    }
  }
}

// getter for obj.AVFrame property

- (AVFrame*) AVFrame
{
  return self.frameObj;
}

@end
